// Copyright 2011 Google Inc. All Rights Reserved

/**
 * @fileoverview Functions for ...
 * @author ccherubino@google.com (Claudio Cherubino)
 */

/**
 * Google Developer Relations namespace.
 */
devrel = {};

/**
 * Classifies namespace.
 */
devrel.classifies = {};

/**
 * Endpoint for the tags.
 */
devrel.classifies.TAGS_URL = "/resources/v1/tags";

/**
 * Endpoint for the entries.
 */
devrel.classifies.ENTRIES_URL = "/resources/v1/items";

/**
 * List of tags.
 */
devrel.classifies.tags = [];

/**
 * Queue for entries that must be classified.
 */
devrel.classifies.NEW_ENTRIES = 'NEW_ENTRIES';

/**
 * Queue for entries that have been skipped.
 */
devrel.classifies.SKIPPED_ENTRIES = 'SKIPPED_ENTRIES';

/**
 * Queue for entries that were already classified.
 */
devrel.classifies.CLASSIFIED_ENTRIES = 'CLASSIFIED_ENTRIES';

/**
 * Queues for entries.
 */
devrel.classifies.queues = {};


/**
 * Performs an XMLHttpRequest to the backend.
 * @param {string} method HTTP method to use.
 * @param {string} url Endpoint to be contacted.
 * @param {string} body Content of the request, if applicable.
 * @param {Function} callback If the response from fetching url has a
 *     HTTP status of 200, this function is called with a JSON decoded
 *     response.  Otherwise, this function is called with null.
 */
devrel.classifies.sendRequest = function(method, url, body, callback) {
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function(data) {
    if (xhr.readyState == 4) {
      if (xhr.status == 200) {
        var data = JSON.parse(xhr.responseText);
        callback(data);
      } else if (xhr.status >= 400 || xhr.status == 0) {
        callback(null);
      }
    }
  };
  xhr.open(method, url, true);
  xhr.send(body);
};

/**
 * Performs an XMLHttpRequest GET to the backend.
 * @param {string} url Endpoint to be contacted.
 * @param {Function} callback If the response from fetching url has a
 *     HTTP status of 200, this function is called with a JSON decoded
 *     response.  Otherwise, this function is called with null.
 */
devrel.classifies.sendGetRequest = function(url, callback) {
  devrel.classifies.sendRequest('GET', url, null, callback);
};

/**
 * Performs an XMLHttpRequest PUT to the backend.
 * @param {string} url Endpoint to be contacted.
 * @param {string} body Content of the request.
 * @param {Function} callback If the response from fetching url has a
 *     HTTP status of 200, this function is called with a JSON decoded
 *     response.  Otherwise, this function is called with null.
 */
devrel.classifies.sendPutRequest = function(url, body, callback) {
  devrel.classifies.sendRequest('PUT', url, body, callback);
};

/**
 * Performs an XMLHttpRequest POST to the backend.
 * @param {string} url Endpoint to be contacted.
 * @param {string} body Content of the request.
 * @param {Function} callback If the response from fetching url has a
 *     HTTP status of 200, this function is called with a JSON decoded
 *     response.  Otherwise, this function is called with null.
 */
devrel.classifies.sendPostRequest = function(url, body, callback) {
  devrel.classifies.sendRequest('POST', url, body, callback);
};

/**
 * Performs an XMLHttpRequest DELETE to the backend.
 * @param {string} url Endpoint to be contacted.
 * @param {Function} callback If the response from fetching url has a
 *     HTTP status of 200, this function is called with a JSON decoded
 *     response.  Otherwise, this function is called with null.
 */
devrel.classifies.sendDeleteRequest = function(url, callback) {
  devrel.classifies.sendRequest('DELETE', url, null, callback);
};

/**
 * Retrieves all tags from the backend.
 * @param {Function} callback The method to call when the request completes.
 */
devrel.classifies.getAllTags = function(callback) {
  devrel.classifies.sendGetRequest(devrel.classifies.TAGS_URL, function(data) {
    // something went wrong server-side
    if (data == null || data['tags'] == null) {
      return;
    }

    // replace tags array
    devrel.classifies.tags = data['tags'];

    // tell the UI we are done
    callback();
  })
};

/**
 * Retrieves new entries from the backend and populates the queue.
 * @param {Function} callback The method to call when the request completes.
 */
devrel.classifies.getNewEntries = function(callback) {
  devrel.classifies.sendGetRequest(devrel.classifies.ENTRIES_URL, function(data) {
    // something went wrong server-side
    if (data == null || data['items'] == null) {
      return;
    }

    // add new entries to the proper queue
    for (i = 0; i < data['items'].length; i++) {
      devrel.classifies.queues[devrel.classifies.NEW_ENTRIES].push(data['items'][i]);
    }

    // tell the UI we are done
    callback();
  })
};

/**
 * Retrieves a single entry from the backend.
 * @param {string} id Entry identifier.
 * @param {Function} callback The method to call when the request completes.
 */
devrel.classifies.getEntry = function(id, callback) {
  var url = devrel.classifies.ENTRIES_URL + "/" + id;
  devrel.classifies.sendGetRequest(url, callback);
};

/**
 * Updates a single entry in the backend.
 * @param {string} id Entry identifier.
 * @param {Object} entry Object representing the entry to be updated.
 * @param {Function} callback The method to call when the request completes.
 */
devrel.classifies.updateEntry = function(id, entry, callback) {
  var url = devrel.classifies.ENTRIES_URL + "/" + id;
  var serialized_entry = JSON.stringify(entry);
  devrel.classifies.sendPutRequest(url, serialized_entry, callback);
};

/**
 * Returns the next entry to be classified.
 * If there are no new entries, its returns one that was previously skipped.
 * If the skipped queue is also empty, it returns null;
 */
devrel.classifies.getNextEntry = function() {
  var new_entries = devrel.classifies.queues[devrel.classifies.NEW_ENTRIES];
  if (new_entries.length > 0) {
    return new_entries.shift();
  }

  var skipped_entries = devrel.classifies.queues[devrel.classifies.SKIPPED_ENTRIES];
  if (skipped_entries.length > 0) {
    return skipped_entries.shift();
  }

  return null;
};

/**
 * Adds the entry to the skipped queue.
 * @param {string} entry Item to be skipped.
 * @return {number} new length of the skipped queue.
 */
devrel.classifies.skipEntry = function(entry) {
  return devrel.classifies.queues[devrel.classifies.SKIPPED_ENTRIES].push(entry);
};

/**
 * Reset (empty) the queues.
 */
devrel.classifies.resetQueues = function() {
  devrel.classifies.queues[devrel.classifies.NEW_ENTRIES] = [];
  devrel.classifies.queues[devrel.classifies.SKIPPED_ENTRIES] = [];
  devrel.classifies.queues[devrel.classifies.CLASSIFIED_ENTRIES] = [];
};

/**
 * Adds a tag to an entry.
 * @param {Object} entry Object representing the entry to be tagged.
 * @param {string} tag Tag to be added.
 */
devrel.classifies.addTagToEntry = function(entry, tag) {
  // only add the tag if it is not already present
  if (entry.tags.indexOf(tag) == -1) {
    entry.tags.push(tag);
  }
};

/**
 * Submit all classified entries to the backend.
 */
devrel.classifies.submitClassifiedEntries = function() {
  var entries = devrel.classifies.queues[devrel.classifies.CLASSIFIED_ENTRIES];
  var stop = false;
  while (entries.length > 0 && !stop) {
    entry = entries.shift();
    devrel.classifies.updateEntry(entry.id, entry, function(data) {
        if (data == null) {
          entries.push(entry);
          stop = true;
        }
      });
  }
};

window.addEventListener('online', function(e) {
  devrel.classifies.submitClassifiedEntries();
}, false);

devrel.classifies.resetQueues();